{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "from array import array\n",
    "import reprlib\n",
    "import math\n",
    "import numbers\n",
    "import functools\n",
    "import itertools\n",
    "import operator\n",
    "\n",
    "\n",
    "class Vector:\n",
    "    typecode = 'd'\n",
    "\n",
    "    def __init__(self, components):\n",
    "        self._components = array(self.typecode, components)\n",
    "\n",
    "    def __iter__(self):\n",
    "        return iter(self._components)\n",
    "\n",
    "    def __repr__(self):\n",
    "        components = reprlib.repr(self._components)\n",
    "        components = components[components.find('['):-1]\n",
    "        return 'Vector({})'.format(components)\n",
    "\n",
    "    def __str__(self):\n",
    "        return str(tuple(self))\n",
    "\n",
    "    def __bytes__(self):\n",
    "        return (bytes([ord(self.typecode)]) +\n",
    "                bytes(self._components))\n",
    "\n",
    "    def __eq__(self, other):\n",
    "        if isinstance(other, Vector):\n",
    "            return (len(self) == len(other) and\n",
    "                    all(a == b for a, b in zip(self, other)))\n",
    "        else:\n",
    "            return NotImplemented\n",
    "        \n",
    "    def __ne__(self, other):\n",
    "        eq_result = self == other\n",
    "        if eq_result is NotImplemented:\n",
    "            return NotImplemented\n",
    "        else:\n",
    "            return not eq_result\n",
    "    \n",
    "    def __hash__(self):\n",
    "        hashes = (hash(x) for x in self._components)\n",
    "        return functools.reduce(operator.xor, hashes, 0)\n",
    "\n",
    "    def __abs__(self):\n",
    "        return math.sqrt(sum(x * x for x in self))\n",
    "    \n",
    "    def __neg__(self):\n",
    "        return Vector(-x for x in self)\n",
    "    \n",
    "    def __pos__(self):\n",
    "        return Vector(self)\n",
    "    \n",
    "    def __add__(self, other):\n",
    "        try:\n",
    "            pairs = itertools.zip_longest(self, other, fillvalue=0.0)\n",
    "            return Vector(a + b for a, b in pairs)\n",
    "        except TypeError:\n",
    "            return NotImplemented\n",
    "    \n",
    "    def __radd__(self, other):\n",
    "        return self + other\n",
    "    \n",
    "    def __mul__(self, scalar):\n",
    "        if isinstance(scalar, numbers.Real):\n",
    "            return Vector(n * scalar for n in self)\n",
    "        else:\n",
    "            return NotImplemented\n",
    "    \n",
    "    def __rmul__(self, scalar):\n",
    "        return self * scalar\n",
    "    \n",
    "    def __matmul__(self, other):\n",
    "        try:\n",
    "            return sum(a * b for a, b in zip(self, other))\n",
    "        except TypeError:\n",
    "            return NotImplemented\n",
    "\n",
    "    def __rmatmul__(self, other):\n",
    "        return self @ other\n",
    "\n",
    "    def __bool__(self):\n",
    "        return bool(abs(self))\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self._components)\n",
    "    \n",
    "    def __getitem__(self, index):\n",
    "        cls = type(self)\n",
    "        if isinstance(index, slice):\n",
    "            return cls(self._components[index])\n",
    "        elif isinstance(index, numbers.Integral):\n",
    "            return self._components[index]\n",
    "        else:\n",
    "            msg = '{cls.__name__} indices must be integers'\n",
    "            raise TypeError(msg.format(cls=cls))\n",
    "# BEGIN VECTOR_V3_GETATTR        \n",
    "    shortcut_names = 'xyzt'\n",
    "    \n",
    "    def __getattr__(self, name):\n",
    "        cls = type(self)\n",
    "        pos = cls.shortcut_names.find(name)\n",
    "        \n",
    "        if 0 <= pos < len(cls.shortcut_names):\n",
    "            return self._components[pos]\n",
    "        msg = '{.__name__!r} object has no attribute {!r}'\n",
    "        raise AttributeError(msg.format(cls, name))\n",
    "# END VECTOR_V3_GETATTR\n",
    "\n",
    "# BEGIN VECTOR_V3_SETATTR\n",
    "    def __setattr__(self, name, value):\n",
    "        cls = type(self)\n",
    "        if len(name) == 1:\n",
    "            if name in cls.shortcut_names:\n",
    "                error = 'readonly attribute {attr_name!r}'\n",
    "            elif name.islower():\n",
    "                error = \"can't set attributes 'a' to 'z' in {cls_name!r}\"\n",
    "            else:\n",
    "                error = ''\n",
    "            if error:\n",
    "                msg = error.format(cls_name=cls.__name__, attr_name=name)\n",
    "                raise AttributeError(msg)\n",
    "        super().__setattr__(name, value)\n",
    "# END VECTOR_V3_SETATTR\n",
    "\n",
    "    @classmethod\n",
    "    def frombytes(cls, octets):\n",
    "        typecode = chr(octets[0])\n",
    "        memv = memoryview(octets[1:]).cast(typecode)\n",
    "        return cls(memv)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "v = Vector([3, 4, 1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Vector([3.0, 4.0, 1.0])"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Vector(v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Vector([4.0, 1.0])"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v[1:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array('d', [3.0, 4.0, 1.0])"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "array('d', v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Vector([-3.0, -4.0, -1.0])"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "-v"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Vector([3.0, 4.0, 1.0])"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "+v"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Vector([13.0, 24.0, 35.0])"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v1 = Vector([3, 4, 5])\n",
    "v2 = v1 + (10, 20, 30)\n",
    "v2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Vector([6.0, 8.0, 10.0])"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Vector([1,2,3]) + Vector([5,6,7])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Vector([4.0, 6.0, 8.0])"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[1,2,3] + v1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Vector([3.0, 4.0, 1.0])"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Vector([3.0, 4.0, 5.0]), Vector([13.0, 24.0, 35.0]))"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v1, v2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "310.0"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v1 @ v2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "va = Vector([1.0, 2.0, 3.0])\n",
    "vb = Vector(range(1, 4))\n",
    "va == vb"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{1.0, 2.0, 3.0}"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "set(va)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "va == (1.0, 2.0, 3.0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Vector([4.0, 6.0, 4.0])"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[1,2,3] +v"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
